package com.bsj.power.pls.queue.service.impl.handle;

import com.alibaba.fastjson.JSON;
import com.bsj.consumerQ.function.HandleRunT;
import com.bsj.power.common.config.cached.*;
import com.bsj.power.common.config.redis.AlarmConstant;
import com.bsj.power.common.config.redis.RedisConstant;
import com.bsj.power.common.config.rocketmq.producer.BaseMqPushUtil;
import com.bsj.power.common.constant.AlarmEnum;
import com.bsj.power.common.constant.MqTopicAndTagConstants;
import com.bsj.power.common.constant.NumericalConstants;
import com.bsj.power.common.def.dto.cardReport.BindCardDTO;
import com.bsj.power.common.def.entity.HighRise;
import com.bsj.power.common.def.entity.alarmReport.AlarmReport;
import com.bsj.power.common.def.entity.job.WorkPersonnel;
import com.bsj.power.common.def.entity.locationDevice.LocationDevice;
import com.bsj.power.common.def.entity.path.Path;
import com.bsj.power.common.def.entity.path.PathPoint;
import com.bsj.power.common.def.entity.sys.SystemConfig;
import com.bsj.power.common.def.handleData.path.MidRemainPathState;
import com.bsj.power.common.def.vo.WebSocketMsgVO;
import com.bsj.power.common.def.vo.pls.httpVo.HttpTagPosVO;
import com.bsj.power.common.mapper.AlarmReportMapper;
import com.bsj.power.common.util.ExceptionUtil;
import com.bsj.power.common.util.FileCreateUtil;
import com.bsj.power.common.util.FindMediaDevUtils;
import com.bsj.power.common.util.StringUtils;
import com.bsj.power.pls.config.cached.HighRiseCached;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author : zhuojie.c
 * @version V1.0
 * @Description: 电子围栏人员停留报警
 * @date Date : 2024年08月27日
 */
@Slf4j
@Service
public class QueuePathRemainAlarmHandle implements HandleRunT<HttpTagPosVO> {

    @Value("${annexFile.path}")
    private String annexPath;

    @Autowired
    private PathCached pathCached;
    @Autowired
    private LocationDeviceCached locationDeviceCached;
    @Autowired
    private BindCardCached bindCardCached;
    @Autowired
    private WorkPersonnelCached workPersonnelCached;
    @Autowired
    private RedisCached redisCached;
    @Autowired
    private BaseMqPushUtil baseMqPushUtil;
    @Resource
    private AlarmReportMapper alarmReportMapper;

    @Override
    public void doRun(List<HttpTagPosVO> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        try {
            List<Path> paths = pathCached.hgetAllPathInfo();
            if (CollectionUtils.isEmpty(paths)) {
                log.warn("人员停留判断未获取到电子围栏信息!");
                return;
            }
            String confStr = redisCached.get(RedisConstant.SYS_CONF_MODE);
            if (StringUtils.isEmpty(confStr)) {
                log.warn("人员停留判断未获取到全局配置项信息!");
                return;
            }
            SystemConfig systemConfig = JSON.parseObject(confStr, SystemConfig.class);
            Integer pathRemainTime = systemConfig.getPathRemainTime();
            if (pathRemainTime == null) {
                log.warn("人员停留判断未获取到停留时长: {}", confStr);
                return;
            }
            //将分钟转换为毫秒
            long remainTime = pathRemainTime * 60 * 1000;
            for (HttpTagPosVO httpTagPosVO : list) {
                //位置点信息，Y为高度,先当做平面，只计算X,Z的范围
                double[] pos = httpTagPosVO.getReducePos();
                int mapId = httpTagPosVO.getMapId();
                if (mapId == 0) {
                    log.warn("无定位设备MapId，设备号: {}", httpTagPosVO.getSn());
                    continue;
                }
                HighRise high = HighRiseCached.getHigh(mapId);
                if (high == null) {
                    log.warn("无系统定义MapId: {}", mapId);
                    continue;
                }
                Integer posSpace = HighRiseCached.getSpace(mapId);
                if (posSpace == 0) {
                    log.warn("设备机组位置未定义: {}", mapId);
                    continue;
                }
                //aX, aY, aZ是根据地图三维模型来做判别,存储还是不变
                double aX = pos[0];
                //人员位置跟3D模型的不一致,人员位置的y应换成第三位
                double aY = pos[2];
                //取反值
                double aZ = -pos[1];
                //真实的人员位置
                double x = pos[0];
                double y = pos[1];
                double z = pos[2];
                //定位时间
                long time = httpTagPosVO.getTime();
                Date date = new Date(time);
                //UWB设备号
                String sn = httpTagPosVO.getSn();
                LocationDevice locationDevice = locationDeviceCached.hgetLocationDeviceInfo(sn);
                if (locationDevice == null) {
                    log.info("设备信息无效不存在: {}", sn);
                    continue;
                }
                //绑卡信息
                BindCardDTO bindCardDTO = bindCardCached.hgetBindCardInfo(RedisConstant.BIND_CARD_REDIS_DEVICE_NAME_KEY + sn);
                if (bindCardDTO == null) {
                    log.info("设备未绑卡不进行围栏停留报警处理{}", sn);
                    continue;
                }
                //作业人员信息
                WorkPersonnel workPersonnel = workPersonnelCached.getWorkPersonnelInfo(bindCardDTO.getEmpNo());
                if (workPersonnel == null) {
                    log.info("未获取到作业人员信息,工号: {}", bindCardDTO.getEmpNo());
                    continue;
                }
                Integer personnelId = workPersonnel.getWorkPersonnelId();
                String personnelName = workPersonnel.getName();
                Integer personType = 2;
                Integer deviceType = locationDevice.getDeviceType();
                String nikeName = locationDevice.getNikeName();
                List<AlarmReport> insertAlarmReport = new ArrayList<>();
                Integer alarmLevel = pathCached.hgetAlarmLevel(AlarmEnum.OTHER_ALARM.getCode());
                for (Path path : paths) {
                    try {
                        //是否禁用
                        Integer isBan = path.getIsBan();
                        if (isBan != null && (isBan).equals(NumericalConstants.ONE)) {
                            continue;
                        }
                        //围栏ID
                        Integer pathId = path.getPathId();
                        //围栏名称
                        String pathName = path.getPathName();
                        // 围栏地址
                        String pathAddress = path.getPathAddress();
                        //围栏空间坐标
                        Integer pathMapId = path.getMapId();
                        if (StringUtils.isEmpty(pathAddress)) {
                            pathAddress = pathName;
                        }
                        if (pathMapId == null) {
                            //围栏信息错误
                            log.info("围栏机组坐标ID不对，无效mapId: {}", pathMapId);
                            continue;
                        }
                        //需要确认空间是否一致
                        //围栏的机组信息
                        Integer pathSpace = HighRiseCached.getSpace(pathMapId);
                        if (!posSpace.equals(pathSpace)) {
                            log.info("围栏机组坐标与设备机组不匹配，设备mapId: {}, 围栏mapId:{}", mapId, pathMapId);
                            continue;
                        }
                        //关联摄像头信息
                        List<Long> cameraIds = pathCached.smemsPathDevice(pathId);
                        //判断是否在该围栏内
                        List<PathPoint> pathPoints = path.getPathPoints();
                        List<String> xyAddList = pathPoints.stream().map(PathPoint::toAddString).collect(Collectors.toList());
                        String areaCoordinates = xyAddList.stream().collect(Collectors.joining("|"));
                        boolean inPoly = FindMediaDevUtils.IsPathMapInPoly(aX, aZ, areaCoordinates, mapId, pathMapId);
//                        boolean inPoly = FindMediaDevUtils.IsPtInPoly(aX, aZ, areaCoordinates);
//                        boolean inPoly = FindMediaDevUtils.IsThreeDInPoly(aX, aZ, aY, areaCoordinates, high.getRiseMin(), high.getRiseMax());;
//                        log.info("设备位置点: {}, 电子围栏信息: {}, 围栏位置: {}, 是否在区域内: {}, 系统高度值: {}",
//                                pos, pathName, areaCoordinates, inPoly, JSON.toJSONString(high));
                        //根据设备号跟围栏ID获取中间状态
                        String remainPathState = redisCached.hget(AlarmConstant.MID_REMAIN_PATH_STATE + sn, pathId + "");
                        MidRemainPathState midRemainPathState;
                        if (remainPathState == null) {
                            midRemainPathState = MidRemainPathState.builder()
                                    .beginTime(time).x(x).y(y).z(z).build();
                        } else {
                            midRemainPathState = JSON.parseObject(remainPathState, MidRemainPathState.class);
                        }
                        int midState = midRemainPathState.getState();
                        if (midState == 0) {
                            //中间状态正常,不在区域内,需要重置首次进入时间
                            if (!inPoly) {
                                midRemainPathState.setBeginTime(0L);
                                redisCached.hset(AlarmConstant.MID_REMAIN_PATH_STATE + sn, pathId + "", JSON.toJSONString(midRemainPathState));
                            } else {
                                // 1.2中间状态正常，本次在区域中,则需要判断首次进入时间到当前时间是否超出设定值
                                long beginTime = midRemainPathState.getBeginTime();
                                if (beginTime == 0) {
                                    //首次进入写入进入时间
                                    midRemainPathState.setBeginTime(time);
                                    beginTime = time;
                                }
                                long diff = time - beginTime;
                                if (diff > remainTime) {
//                                    log.info("触发异常停留报警,设备定位时间: {}, 首次进入时间: {}, 设置的异常停留时间: {}, 时长： {}",
//                                            time, beginTime, remainTime, diff);
                                    //在围栏停留的时长超过了设置的限制,则需要进行异常停留报警触发
                                    AlarmReport alarmReport = new AlarmReport(sn, personnelId, pathId,
                                            nikeName, deviceType, personnelName, pathName, 3,
                                            time, Arrays.toString(pos), 1, personType);
                                    alarmReport.setCameraIds(cameraIds);
                                    alarmReport.setPathAddress(pathAddress);
                                    alarmReport.setAlertLevel(alarmLevel);
                                    //缓存报警状态
                                    redisCached.hset(RedisConstant.PATH_REMAIN_ALARM_KEY + sn,
                                            pathId + "", JSON.toJSONString(alarmReport));
                                    // 修改围栏异常停留报警中间状态
                                    midRemainPathState.setState(NumericalConstants.ONE);
                                    midRemainPathState.setAlarmTime(time);
                                    midRemainPathState.setX(x);
                                    midRemainPathState.setY(y);
                                    midRemainPathState.setZ(z);
//                                    redisCached.hset(AlarmConstant.MID_REMAIN_PATH_STATE + sn, pathId + "", JSON.toJSONString(midRemainPathState));
                                    //报警附件创建
                                    FileCreateUtil.addAnnexFileAdd(new Date(time), cameraIds, annexPath, baseMqPushUtil, alarmReport, false);
                                    //报警信息推送
                                    WebSocketMsgVO webSocketMsgVO = WebSocketMsgVO.builder()
                                            .msgType(NumericalConstants.TWO)
                                            .obj(alarmReport)
                                            .build();
                                    baseMqPushUtil.sendTopicBytes(MqTopicAndTagConstants.SYSTEM_WEB_SERVER_CHANGE_TOPIC,
                                            MqTopicAndTagConstants.SYSTEM_WEB_PRODUCT_CHANGE_TAG, JSON.toJSONBytes(webSocketMsgVO));
                                }
                                // 1.3中间状态正常，本次在区域中
                                redisCached.hset(AlarmConstant.MID_REMAIN_PATH_STATE + sn, pathId + "", JSON.toJSONString(midRemainPathState));
                            }
                        } else if (midState == 1) {
                            //中间状态不正常, 本次不在区域里,需要结束报警并进行数据记录，并修改中间状态
                            if (!inPoly && midRemainPathState.getAlarmTime() != 0) {
                                AlarmReport alarmReport = new AlarmReport(sn, personnelId, pathId,
                                        nikeName, deviceType, personnelName, pathName, 3,
                                        time, Arrays.toString(pos), 1, personType);
                                Date startTime = new Date(midRemainPathState.getAlarmTime());
                                alarmReport.setBeginTime(startTime);
                                alarmReport.setEndTime(new Date(time));
                                //开始与结束的位置点
                                alarmReport.setPos(Arrays.toString(new Double[]{midRemainPathState.getX(), midRemainPathState.getY(), midRemainPathState.getZ()}));
                                alarmReport.setEndPos(Arrays.toString(pos));
                                //报警附件创建
                                FileCreateUtil.addAnnexFileAdd(startTime, cameraIds, annexPath, baseMqPushUtil, alarmReport, true);
                                insertAlarmReport.add(alarmReport);
                                //恢复中间位置状态,重置进入时间为0
                                midRemainPathState.setState(NumericalConstants.ZERO);
                                midRemainPathState.setBeginTime(0L);
                                midRemainPathState.setX(x);
                                midRemainPathState.setY(y);
                                midRemainPathState.setZ(z);
                                redisCached.hset(AlarmConstant.MID_REMAIN_PATH_STATE + sn, pathId + "", JSON.toJSONString(midRemainPathState));
                                //删除缓存中的报警数据
                                redisCached.hdel(RedisConstant.PATH_REMAIN_ALARM_KEY + sn, pathId + "");
                                //发送消息通知组合报警
                                baseMqPushUtil.sendTopicBytes(MqTopicAndTagConstants.BASE_SERVER_PLS_CHANGE_TOPIC,
                                        MqTopicAndTagConstants.BASE_CHANGE_PLS_UPDATE_TAG, JSON.toJSONBytes(alarmReport));
                            }
                        }
                    } catch (Exception e) {
                        log.info("电子围栏停留判断错误: {}, 异常信息: {}", ExceptionUtil.getStackStr(e));
                        continue;
                    }
                }
                try {
                    if (!insertAlarmReport.isEmpty()) {
                        //报表存储
                        insertAlarmReport.stream().forEach(item -> {
                            alarmReportMapper.insert(item);
                        });
                    }
                } catch (Exception e) {
                    log.info("围栏异常停留报警存储错误: {}, 异常信息: {}", ExceptionUtil.getStackStr(e));
                    continue;
                }
            }
        } catch (Exception e) {
            log.error("QueuePathRemainAlarmHandle 异常：{}", ExceptionUtil.getStackStr(e));
        }
    }

    public static void main(String[] args) {
        long beginTime = 1725798854760L;
        long millis = System.currentTimeMillis();
        System.out.println(millis - beginTime);
        //10分钟
        long remainTime = 10 * 60 * 1000;
        long diff = millis - beginTime;
        if (diff > remainTime) {
            System.out.println(true);
        } else {
            System.out.println(false);
        }
    }
}
